/**
 * Copyright [2019] [LiBo/Alex of copyright liboware@gmail.com ]
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.dess.mybatis.plugins.execute;

import com.znlh.demo.mybatisdemo.parser.LocalFieldParser;
import com.znlh.demo.mybatisdemo.parser.LocalInvokeParser;
import com.znlh.demo.mybatisdemo.thread.ThreadContextHolder;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.Properties;

/**
 * @project-name:mybatis-demo
 * @package-name:com.znlh.demo.mybatisdemo.execute
 * @author:LiBo/Alex
 * @create-date:2020-06-16 19:37
 * @copyright:libo-alex4java
 * @email:liboware@gmail.com
 * @description:
 */
@Slf4j
@Component
@Intercepts({
        @Signature(
                type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class
        })
})
public class CustomStateHandlerInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        CustomExecutorObject customExecutorObject = CustomExecutorUtils.createBean();
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //id为执行的mapper方法的全路径名，如com.uv.dao.UserMapper.insertUser
        String id = mappedStatement.getId();
        //sql语句类型 select、delete、insert、update
        String sqlCommandType = mappedStatement.getSqlCommandType().toString();
        BoundSql boundSql = statementHandler.getBoundSql();
        //获取到原始sql语句
        String sql = boundSql.getSql();
        String mSql = sql;
        //TODO 修改位置
        //注解逻辑判断  添加注解了才拦截
//        Class<?> classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
//        String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
        /*for (Method method : classType.getDeclaredMethods()) {
            if (method.isAnnotationPresent(InterceptAnnotation.class) && mName.equals(method.getName())) {
                InterceptAnnotation interceptorAnnotation = method.getAnnotation(InterceptAnnotation.class);
                if (interceptorAnnotation.flag()) {
                    mSql = sql + " limit 2";
                }
            }
        }*/
        String callType = customExecutorObject.getCondtionCallerType();
        switch (callType){
            case "local":
                String data = String.valueOf(customExecutorObject.getCondtionValue());
                if(data.startsWith("@[")){
                    Object value = LocalFieldParser.invoke(LocalFieldParser.parse(data),customExecutorObject.getConditionSubject());
                    customExecutorObject.setCondtionValue(value);
                }else{
                    Object value = LocalInvokeParser.invoke(LocalInvokeParser.parse(data),customExecutorObject.getConditionSubject(),false);
                    customExecutorObject.setCondtionValue(value);
                }
                break;
            case "remote":
                data = String.valueOf(customExecutorObject.getCondtionValue());
                Object value = LocalInvokeParser.invoke(LocalInvokeParser.parse(data),customExecutorObject.getConditionSubject(),true);
                customExecutorObject.setCondtionValue(value);
                break;
        }
        try {
            if(CustomExecutorUtils.isSelectSql(mappedStatement)){
                log.info("读取获取原始SQL语句信息:{}",mSql);
                Statement statement = CCJSqlParserUtil.parse(mSql);
                Select select = (Select) statement;
                SelectBody selectBody = select.getSelectBody();
                if(selectBody instanceof PlainSelect){
                    PlainSelect plainSelect = (PlainSelect) selectBody;
                    if (Objects.nonNull(plainSelect.getWhere())) {
                        log.info("解析操作where条件语句操作服务:{}", plainSelect.getWhere().toString());
                        // 简单的进行控制 未来会考虑复杂场景
                        String newWhereStr = MessageFormat.format(plainSelect.getWhere().toString() + " and {0} {1} {2}",
                                customExecutorObject.getConditonKey(),customExecutorObject.getCondtionToken(),customExecutorObject.getCondtionValue());
                        Expression newWhereObject = (CCJSqlParserUtil.parseCondExpression(newWhereStr));
                        plainSelect.setWhere(newWhereObject);
                        log.info("完成包装或者拼接的sql语句{}",plainSelect.toString());
                        MetaObject metaObject2 = SystemMetaObject.forObject(statementHandler);
                        metaObject2.setValue("delegate.boundSql.sql", plainSelect.toString());
                        //通过反射修改sql语句
                        Field field = boundSql.getClass().getDeclaredField("sql");
                        field.setAccessible(true);
                        field.set(boundSql, plainSelect.toString());
                        return invocation.proceed();
                        //return doSubSelect(plainSelect,customExecutorObject,metaObject,invocation);
                    }else{
                        //排除 无where条件的
                        return invocation.proceed();
                    }
                }
            }
        } catch (Exception e) {
            log.error("执行定制化sql拦截器失败！",e);
            return invocation.proceed();
        } finally {
            ThreadContextHolder.remove(CustomExecutorContext.getTHREAD_LOCAL_EXECUTOR_CONTEXT());
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }

    }

    @Override
    public void setProperties(Properties properties) {

    }
}
